import { Context, HonoRequest } from "hono";
import { retryRequest } from "./retryHandler";
import Providers from "../providers";
import { ANTHROPIC, MAX_RETRIES, HEADER_KEYS, RETRY_STATUS_CODES, POWERED_BY, RESPONSE_HEADER_KEYS, AZURE_OPEN_AI } from "../globals";
import { responseHandler, updateResponseHeaders } from "./handlerUtils";
import { env } from "hono/adapter";
// Find the proxy provider
function proxyProvider(proxyModeHeader:string, providerHeader: string) {
  const proxyProvider = proxyModeHeader?.split(" ")[1] ?? providerHeader;
  return proxyProvider;
}

function getProxyPath(requestURL:string, proxyProvider:string, proxyEndpointPath:string) {
  let reqURL = new URL(requestURL);
  let reqPath = reqURL.pathname;
  const reqQuery = reqURL.search;
  reqPath = reqPath.replace(proxyEndpointPath, "");
  const providerBasePath = Providers[proxyProvider].api.baseURL;
  if (proxyProvider === AZURE_OPEN_AI) {
    return `https:/${reqPath}${reqQuery}`;
  }
  let proxyPath = `${providerBasePath}${reqPath}${reqQuery}`;
  
  // Fix specific for Anthropic SDK calls. Is this needed? - Yes
  if (proxyProvider === ANTHROPIC) {
      proxyPath = proxyPath.replace("/v1/v1/", "/v1/");
  }
  
  return proxyPath;
}


function headersToSend(headersObj: Record<string, string>, customHeadersToIgnore: Array<string>): Record<string, string> {
  let final: Record<string, string> = {};
  const poweredByHeadersPattern = `x-${POWERED_BY}-`;
  const headersToAvoid = [...customHeadersToIgnore]
  headersToAvoid.push("content-length");
  Object.keys(headersObj).forEach((key: string) => {
    if (!headersToAvoid.includes(key) && !key.startsWith(poweredByHeadersPattern)) {
      final[key] = headersObj[key];
    }
  });
  final['accept-encoding'] = "gzip, deflate"
  return final;
}

export async function proxyGetHandler(c: Context): Promise<Response> {
  try {
    const requestHeaders = Object.fromEntries(c.req.headers);
    delete requestHeaders["content-type"];
    const store: Record<string, any> = {
      proxyProvider: proxyProvider(requestHeaders[HEADER_KEYS.MODE], requestHeaders[HEADER_KEYS.PROVIDER]),
      customHeadersToAvoid: env(c).CUSTOM_HEADERS_TO_IGNORE ?? [],
      reqBody: {},
      proxyPath: c.req.url.indexOf("/v1/proxy") > -1 ? "/v1/proxy" : "/v1"
    }

    let urlToFetch = getProxyPath(c.req.url, store.proxyProvider, store.proxyPath);

    let fetchOptions = {
        headers: headersToSend(requestHeaders, store.customHeadersToAvoid),
        method: c.req.method
    };

    let retryCount = Math.min(parseInt(requestHeaders[HEADER_KEYS.RETRIES])||1, MAX_RETRIES);

    let [lastResponse, lastAttempt] = await retryRequest(urlToFetch, fetchOptions, retryCount, RETRY_STATUS_CODES);

    const mappedResponse = await responseHandler(lastResponse, store.isStreamingMode, store.proxyProvider, undefined, urlToFetch);
    updateResponseHeaders(mappedResponse, 0, store.reqBody, "DISABLED", lastAttempt ?? 0, requestHeaders[HEADER_KEYS.TRACE_ID] ?? "");

    c.set("requestOptions", [{
      providerOptions: {...store.reqBody, provider: store.proxyProvider, requestURL: urlToFetch, rubeusURL: 'proxy'},
      requestParams: store.reqBody,
      response: mappedResponse.clone(),
      cacheStatus: "DISABLED",
      cacheKey: undefined
    }])

    return mappedResponse;
  } catch (err: any) {
    console.log("proxyGet error", err.message);
    return new Response(
        JSON.stringify({
            status: "failure",
            message: "Something went wrong",
        }), {
          status: 500,
          headers: {
              "content-type": "application/json"
          }
      }
    );
  } 
}
